package birdsong.comm;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.LinkedList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import birdsong.comm.ClientSideMessage.Type;

/**
 * Communication manager for {@link birdsong.client.BirdsongServer Birdsong
 * server}. Birdsong server have this object to manage all the messages sent by
 * the clients.
 * 
 * @author While True
 * @version 1
 */

public class DealWithClient {

        private BlockingQueue<ClientMessage> serverSenderMessageList = new LinkedBlockingQueue<ClientMessage>();
        private LinkedList<Thread> listThreadsActive = new LinkedList<Thread>(); 
        private ObjectOutputStream out;
        private ObjectInputStream in;
        private final Socket socket;
        private String clientNickname;
        private DealWithClient dealer = this;

        private final ServerCommunicator serverCommunicator;

        /**
         * Constructor that receives a socket and a ServerCommunicator. Will
         * generate threads to take care of {@link OutputStream} and
         * {@link InputStream}. Will add all threads generated to listThreadsActive
         * for interruption purposes.
         * 
         * @param socket
         *            Socket for the network interface.
         * @param clientCommunicator
         *            ClientCommunicator responsible for the communication of the
         *            client.
         * @throws IOException
         *             Send an exception if the socket is closed.
         */
        public DealWithClient(Socket socket, ServerCommunicator serverCommunicator) throws IOException {
                this.socket = socket;
                this.serverCommunicator = serverCommunicator;
                out = new ObjectOutputStream(socket.getOutputStream());
                in = new ObjectInputStream(socket.getInputStream());
                getListThreadsActive().add(new ThreadSenderClient());
                getListThreadsActive().add(new ThreadReceiverClient());
                
        }

        private class ThreadReceiverClient extends Thread {  
                @Override
                public void run() {
                        try { 
                                while (!isInterrupted()) { 
                                        ServerMessage message = (ServerMessage) getIn().readObject();
                                        synchronized (serverCommunicator) {
                                                if (message.getType().equals(ServerSideMessage.Type.CLIENT_CONNECTED)) {
                                                        if (getServerCommunicator().getMapDealWithClients().get(message.getClientNickname()) == null) {
                                                                setClientNickname(message.getClientNickname());
                                                                getServerCommunicator().getMapDealWithClients().put(getClientNickname(), getDealer());
                                                                serverCommunicator.getServerMessageListReceived().add(message);
                                                        } else {
                                                                out.writeObject(new ClientMessage(Type.ERROR, "ERROR: client already exists", clientNickname));
                                                                out.close();
                                                                in.close();
                                                        }
                                                } else 
                                                        serverCommunicator.getServerMessageListReceived().add(message); 
                                        }
                                }
                        } catch (Exception e) {}
                }
        }
 
        private class ThreadSenderClient extends Thread { 
                @Override
                public void run() { 
                        try {
                                while (!isInterrupted()) { 
                                        ClientMessage message = getServerSenderMessageList().take();
                                        getOut().writeObject(message);
                                }
                        } catch (Exception e) {}
                        
                }
        }

        /**
         * Starts all created threads.
         */
        public void start() {
                for (Thread listThreadsActive : getListThreadsActive()) {
                        listThreadsActive.start();
                } 
        }

        /**
         * Getter that returns the socket.
         * 
         * @return Returns the socket.
         */
        public Socket getSocket() {
                return socket;
        }

        /**
         * Getter that returns the ServerCommunicator.
         * 
         * @return Returns the ServerCommunicatort.
         */
        public ServerCommunicator getServerCommunicator() {
                return serverCommunicator;
        }

        /**
         * Getter that returns the OutputStreamer.
         * 
         * @return Returns the ObjectInputStream.
         */
        public ObjectOutputStream getOut() {
                return out;
        }

        /**
         * Getter that returns the InputStreamer.
         * 
         * @return Returns the ObjectInputStream.
         */
        public ObjectInputStream getIn() {
                return in;
        }

        /**
         * Setter that sets the client nickname.
         */
        public void setClientNickname(String clientNickname) {
                this.clientNickname = clientNickname;
        }

        /**
         * Getter that returns the client nickname.
         * 
         * @return Returns the clientNickname.
         */
        public String getClientNickname() {
                return clientNickname;
        }

        /**
         * Getter that returns the list of messages to be sent to the client.
         * 
         * @return Returns the serverSenderMessageList.
         */
        public BlockingQueue<ClientMessage> getServerSenderMessageList() {
                return serverSenderMessageList;
        }

        /**
         * Getter that returns the list of active threads.
         * 
         * @return Returns the listThreadsActive.
         */
        public LinkedList<Thread> getListThreadsActive() {
                return listThreadsActive;
        }

        /**
         * Getter that returns itself.
         * 
         * @return Returns the instance of the class.
         */
        public DealWithClient getDealer() {
                return dealer;
        }

}